package com.polarbookshop.orderservice.order.domain

import com.polarbookshop.orderservice.book.Book
import com.polarbookshop.orderservice.book.BookClient
import com.polarbookshop.orderservice.order.event.OrderAcceptedMessage
import com.polarbookshop.orderservice.order.event.OrderDispatchedMessage
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.cloud.stream.function.StreamBridge
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

private val log = LoggerFactory.getLogger(OrderService::class.java)

@Service
class OrderService {

    @Autowired
    private lateinit var orderRepository: OrderRepository

    @Autowired
    private lateinit var bookClient: BookClient

    @Autowired
    private lateinit var streamBridge: StreamBridge

    fun getAllOrders(userId: String): Flux<Order> {
        return orderRepository.findAllByCreatedBy(userId)
    }

    @Transactional
    fun submitOrder(isbn: String, quantity: Int): Mono<Order> {
        return bookClient.getBookByIsbn(isbn)
            .map { buildAcceptedOrder(it, quantity) }
            .defaultIfEmpty(
                buildRejectedOrder(isbn, quantity)
            )
            .flatMap { orderRepository.save(it) }
            .doOnNext { publishOrderAcceptedEvent(it) }
    }

    fun consumeOrderDispatchedEvent(flux: Flux<OrderDispatchedMessage>): Flux<Order> =
        flux.flatMap { orderRepository.findById(it.orderId) }
            .map { buildDispatchedOrder(it) }
            .flatMap { orderRepository.save(it) }

    fun publishOrderAcceptedEvent(order: Order) {
        if (order.status != OrderStatus.ACCEPTED) {
            return
        }
        val orderAcceptedMessage = order.id?.let { OrderAcceptedMessage(it) }
        log.info("Sending order accepted event with id {}", order.id)
        val result = streamBridge.send("acceptOrder-out-0", orderAcceptedMessage)
        log.info("Result of sending data for order with id {}: {}", order.id, result)
    }

    companion object {
        fun buildDispatchedOrder(existingOrder: Order): Order =
            Order(
                existingOrder.id,
                existingOrder.bookIsbn,
                existingOrder.bookName,
                existingOrder.bookPrice,
                existingOrder.quantity,
                OrderStatus.DISPATCHED,
                existingOrder.createdDate,
                existingOrder.lastModifiedDate,
                existingOrder.createdBy,
                existingOrder.lastModifiedBy,
                existingOrder.version
            )

        fun buildRejectedOrder(bookIsbn: String, quantity: Int): Order {
            return Order(bookIsbn, null, null, quantity, OrderStatus.REJECTED);
        }

        fun buildAcceptedOrder(book: Book, quantity: Int): Order {
            return Order(book.isbn, "${book.title} - ${book.author}", book.price, quantity, OrderStatus.ACCEPTED)
        }
    }
}